#include <core/types.h>
#include <core/irq.h>
#include <core/io.h>
#include <malloc.h>
#include <core/errno.h>
#include <autoconf.h>

static struct IrqDesc gIrqDesc[MAX_IRQ_NUM];

static int HandleDevIrqList(struct IrqDev *pIrqDev);

int RegisterIsr(UINT32 nIrq, DEV_IRQ_HANDLE DevHandle, void *pDevice)
{
	struct IrqDev *pIrqDev, **p;
	struct IrqDesc *pDesc;


	if (nIrq >= MAX_IRQ_NUM)
		return -EINVAL;

	pDesc = gIrqDesc + nIrq;

	pIrqDev = malloc(sizeof(struct IrqDev));
	if (!pIrqDev)
		return -ENOMEM;

	pIrqDev->pDevice   = pDevice;
	pIrqDev->DevHandle = DevHandle;
	pIrqDev->pNextDev  = NULL;

	p = &pDesc->pIrqDev;
	while (*p)
	{
		p = &(*p)->pNextDev;
	}

	*p = pIrqDev;

	pDesc->pIntCtrl->Unmask(nIrq);

	return 0;
}


void DoIRQ(UINT32 nIrq)
{
	struct IrqDesc *pDesc;

	BUG_IF(nIrq >= MAX_IRQ_NUM);

//	printf("%s(): nIrq = %d\n", __FUNCTION__, nIrq);

	pDesc = gIrqDesc + nIrq;

	pDesc->IrqHandle(pDesc, nIrq);

#if defined(CONF_AT91SAM9261) | defined(CONF_AT91SAM9263)
	At91AicWrite(AIC_EOICR, 1);
#endif
}

///////////

int IrqCtrlAssociate(UINT32 nIrq, struct IntCtrl *pIntCtrl)
{
	struct IrqDesc *pDesc = gIrqDesc + nIrq;

	pDesc->pIntCtrl = pIntCtrl;

	return 0;
}


int IrqSetTriggerType(UINT32 nIrq, UINT32 nType)
{
	struct IrqDesc *pDesc;

	pDesc = gIrqDesc + nIrq;

	return pDesc->pIntCtrl->SetTrigger(nIrq, nType);
}

// fixme
void IrqHandleLevel(struct IrqDesc *pDesc, UINT32 nIrq)
{
	struct IrqDev *pIrqDev = pDesc->pIrqDev;

	pDesc->pIntCtrl->MaskAck(nIrq);

	BUG_IF(NULL == pIrqDev);

	HandleDevIrqList(pIrqDev);

	pDesc->pIntCtrl->Unmask(nIrq);
}


void IrqHandleEdge(struct IrqDesc *pDesc, UINT32 nIrq)
{
	struct IrqDev *pIrqDev = pDesc->pIrqDev;

	pDesc->pIntCtrl->Ack(nIrq);

	BUG_IF(NULL == pIrqDev);

	HandleDevIrqList(pIrqDev);
}


void IrqHandleSimple(struct IrqDesc *pDesc, UINT32 nIrq)
{
	struct IrqDev *pIrqDev = pDesc->pIrqDev;


	BUG_IF(NULL == pIrqDev);

	HandleDevIrqList(pIrqDev);
}


void IrqSetHandler(UINT32 nIrq, IRQ_HANDLE IrqHandle, int isChained)
{
	struct IrqDesc *pDesc;

	pDesc = gIrqDesc + nIrq;

	pDesc->IrqHandle = IrqHandle;

	if (isChained)
	{
		pDesc->pIntCtrl->Unmask(nIrq);
	}
}


static int HandleDevIrqList(struct IrqDev *pIrqDev)
{
	int ret, retval = IRQ_NONE;

	do
	{
		ret = pIrqDev->DevHandle(pIrqDev->pDevice);
		retval |= ret;
		pIrqDev = pIrqDev->pNextDev;
	} while (pIrqDev);

	return retval;
}

#if 0
void DoFIQ(void)
{
	printf("%s()\n", __FUNCTION__);
}
#endif

